/**
 * @(#)SystemProcessDefinitionServiceImpl.java Aug 31, 2009
 *   
 * Copyright  (c) 2009-2010 Wilmar IT Service,  Inc.   
 * All  rights  reserved.   
 * TODO
 */

package com.wilmar.process.core.services.impl;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipInputStream;

import org.apache.log4j.Logger;
import org.jbpm.api.NewDeployment;
import org.jbpm.api.ProcessDefinition;
import org.jbpm.api.ProcessDefinitionQuery;
import org.jbpm.api.ProcessEngine;
import org.jbpm.api.ProcessInstance;

import com.wilmar.common.util.PageinationEntity;
import com.wilmar.process.core.ProcessSystemDeclareKeys;
import com.wilmar.process.core.services.api.ISystemProcessAuthorityService;
import com.wilmar.process.core.services.api.ISystemProcessDefinitionService;
import com.wilmar.process.core.services.api.ISystemProcessReleaseNotesService;
import com.wilmar.process.entity.ProcessReleaseNotes;
import com.wilmar.process.entity.User;
import com.wilmar.process.entity.query.ProcessDefinitionQueryObject;
import com.wilmar.process.entity.vo.ProcessDefinitionVO;
import com.wilmar.process.exception.DirtyDataException;
import com.wilmar.process.exception.ObjectNotFoundException;
import com.wilmar.process.exception.PermissionDeniedException;

/**
 * 系统流程定义服务实现
 * 
 * @author MaKuBEX Email:makubexnet@gmail.com
 * @version 2.0.1
 */
public class SystemProcessDefinitionServiceImpl implements
		ISystemProcessDefinitionService {
	private static final Logger log = Logger.getLogger
			(SystemProcessDefinitionServiceImpl.class);

	private ProcessEngine processEngine;
	private ISystemProcessReleaseNotesService systemProcessReleaseNotesService;
	private ISystemProcessAuthorityService systemProcessAuthorityService;

	/**
	 * @see com.wilmar.process.core.services.api.ISystemProcessDefinitionService#deployProcessDefinition(java.io.File)
	 */
	public void deployProcessDefinition(File definitionPackage,
			ProcessReleaseNotes processReleaseNotes) {
		FileInputStream fis;
		try {
			fis = new FileInputStream(definitionPackage);
			ZipInputStream zis = new ZipInputStream(fis);
			String deploymentId = this.processEngine.getRepositoryService()
					.createDeployment().addResourcesFromZipInputStream(zis)
					.deploy();
			List<ProcessDefinition> pdList = this.processEngine
					.getRepositoryService().createProcessDefinitionQuery()
					.deploymentId(deploymentId).list();
			for (ProcessDefinition pd : pdList) {
				log.debug("Deploying process definition: " + pd.getKey());
				ProcessReleaseNotes note = processReleaseNotes;
				note.setProcessDefinitionId(pd.getId());
				note.setCreateTime(new Date());
				systemProcessReleaseNotesService.saveReleaseNotes(note);
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
	}

	public InputStream getDefinitionImageById(String definitionId)
			throws ObjectNotFoundException {
		ProcessDefinition pd = this.processEngine.getRepositoryService()
				.createProcessDefinitionQuery().processDefinitionId(
						definitionId).uniqueResult();
		if (pd == null)
			throw new ObjectNotFoundException(
					"There is no process defined by id : " + definitionId);
		InputStream is = this.processEngine.getRepositoryService()
				.getResourceAsStream(pd.getDeploymentId(),
						pd.getImageResourceName());
		if (is == null)
			throw new ObjectNotFoundException(
					"There is no process definition image with this process definition id : "
							+ definitionId);
		return is;
	}

	/**
	 * @see com.wilmar.process.core.services.api.ISystemProcessDefinitionService#getListAllWithLastestVersion()
	 */
	public List<ProcessDefinition> getListAllWithLastestVersion(
			PageinationEntity page) {
		List<ProcessDefinition> all = this.processEngine.getRepositoryService()
				.createProcessDefinitionQuery().notSuspended().list();

		return this.doFilterLastVersion(all);
	}

	/**
	 * @throws DirtyDataException
	 * @see com.wilmar.process.core.services.api.ISystemProcessDefinitionService#getListAllWithLastestVersion(com.wilmar.process.entity.User,
	 *      java.util.Set, java.util.Set,
	 *      com.wilmar.common.util.PageinationEntity)
	 */
	public List<ProcessDefinition> getListAllWithLastestVersion(User user,
			ProcessDefinitionQueryObject prototype) throws DirtyDataException {
		ProcessDefinitionQuery query = this.processEngine
				.getRepositoryService().createProcessDefinitionQuery()
				.notSuspended();
		if (prototype != null) {
			if (prototype.getProcessDefinitionId() != null
					&& prototype.getProcessDefinitionId().trim().length() != 0)
				query.processDefinitionId(prototype.getProcessDefinitionId());
			if (prototype.getProcessDefinitionKey() != null
					&& prototype.getProcessDefinitionKey().trim().length() != 0)
				query.processDefinitionKey(prototype.getProcessDefinitionKey());
			if (prototype.getProcessDefinitionName() != null
					&& prototype.getProcessDefinitionName().trim().length() != 0)
				query
						.processDefinitionNameLike(ProcessSystemDeclareKeys.PERCENTAGE
								+ prototype.getProcessDefinitionName()
								+ ProcessSystemDeclareKeys.PERCENTAGE);
			if (prototype.getPageination() != null
					&& prototype.getPageination().getLimit() != 0)
				query.page(prototype.getPageination().getStart(), prototype
						.getPageination().getLimit());
		}
		List<ProcessDefinition> all = query.list();
		List<ProcessDefinition> filtered = this.doFilterLastVersion(all);
		List<ProcessDefinition> finalList = new ArrayList<ProcessDefinition>();
		for (ProcessDefinition pd : filtered) {
			try {
				this.systemProcessAuthorityService.validateStartAuthority(pd
						.getKey(), user);
				finalList.add(pd);
			} catch (PermissionDeniedException e) {
			} catch (DirtyDataException e) {
			}
		}
		return finalList;
	}

	/**
	 * 过滤流程定义列表,仅保留最新版本的流程定义列表
	 * 
	 * @param sourceList
	 *            原始流程定义列表
	 * @return 过滤后的流程定义列表
	 */
	private List<ProcessDefinition> doFilterLastVersion(
			List<ProcessDefinition> sourceList) {
		Map<String, ProcessDefinition> tmpMap = new HashMap<String, ProcessDefinition>();
		for (ProcessDefinition pd : sourceList) {
			ProcessDefinition v_pd = tmpMap.get(pd.getName());
			if (v_pd == null) {
				tmpMap.put(pd.getName(), pd);
				continue;
			} else if (pd.getVersion() > v_pd.getVersion()) {
				tmpMap.put(pd.getName(), pd);
				continue;
			}
		}
		List<ProcessDefinition> filteredList = new ArrayList<ProcessDefinition>(
				tmpMap.values());
		return filteredList;
	}

	/**
	 * @see com.wilmar.process.core.services.api.ISystemProcessDefinitionService#getStartFormByProcessDefinitionId(java.lang.String)
	 */
	public String getStartFormByProcessDefinitionId(String processDefinitionId)
			throws ObjectNotFoundException {
		List<String> startNames = this.processEngine.getRepositoryService()
				.getStartActivityNames(processDefinitionId);
		int startNodeCount = startNames.size();
		if (startNodeCount == 0)
			throw new ObjectNotFoundException(
					"This process definition didn't set any start node!");
		else if (startNodeCount > 1) {
			log
					.warn("There is more than 1 start node definded in this process definition!");
		}
		return this.processEngine.getRepositoryService()
				.getStartFormResourceName(processDefinitionId,
						startNames.get(0));
	}

	public ISystemProcessReleaseNotesService getSystemProcessReleaseNotesService() {
		return systemProcessReleaseNotesService;
	}

	/**
	 * @see com.wilmar.process.core.services.api.ISystemProcessDefinitionService#removeAllVersionOfProcessDefinitionByKey(java.lang.String)
	 */
	@SuppressWarnings("static-access")
	public void removeAllVersionOfProcessDefinitionByKey(
			String processDefinitionKey) {

		List<ProcessDefinition> processDefinitions = this.processEngine
				.getRepositoryService().createProcessDefinitionQuery()
				.processDefinitionKey(processDefinitionKey).list();

		for (ProcessDefinition processDefinition : processDefinitions) {

			String processDefinitionId = processDefinition.getId();
			List<ProcessInstance> processInstances = this.processEngine
					.getExecutionService().createProcessInstanceQuery()
					.processDefinitionId(processDefinitionId).list();
			for (ProcessInstance processInstance : processInstances) {
				if (!processInstance.isEnded() || processInstance.isSuspended()) {
					this.processEngine.getExecutionService()
							.endProcessInstance(processInstance.getId(),
									processInstance.STATE_ENDED);
				}
			}

			this.processEngine.getRepositoryService().deleteDeployment(
					processDefinition.getDeploymentId());
		}

	}

	/**
	 * @see com.wilmar.process.core.services.api.ISystemProcessDefinitionService#removeLastestVersionOfProcessDefinitionByKey(java.lang.String)
	 */
	public void removeLastestVersionOfProcessDefinitionByKey(
			String processDefinitionKey) {
		// TODO Auto-generated method stub
	}

	/**
	 * @see com.wilmar.process.core.services.api.ISystemProcessDefinitionService#removeAllInfoProcessDefinitionById(java.lang.String)
	 */
	public void removeAllInfoProcessDefinitionById(String processDefinitionId) {
		// TODO Auto-generated method stub
	}

	/**
	 * @see com.wilmar.process.core.services.api.ISystemProcessDefinitionService#removeProcessDefinitionById(java.lang.String,
	 *      java.lang.String)
	 */
	public void removeProcessDefinitionById(String processDefinitionId,
			String processDeploymentId) {

		List<ProcessInstance> processInstances = this.processEngine
				.getExecutionService().createProcessInstanceQuery()
				.processDefinitionId(processDefinitionId).list(); // notSuspended()
		for (ProcessInstance processInstance : processInstances) {
			if (!processInstance.isEnded() || processInstance.isSuspended()) {
				this.processEngine.getExecutionService().endProcessInstance(
						processInstance.getId(), ProcessInstance.STATE_ENDED);
			}
		}
		this.processEngine.getRepositoryService().deleteDeployment(
				processDeploymentId);
	}

	public void setSystemProcessReleaseNotesService(
			ISystemProcessReleaseNotesService systemProcessReleaseNotesService) {
		this.systemProcessReleaseNotesService = systemProcessReleaseNotesService;
	}

	/**
	 * @see com.wilmar.process.core.services.api.IBaseService#convertToVO(java.lang.Object)
	 */
	public ProcessDefinitionVO convertToVO(ProcessDefinition entity) {
		ProcessDefinitionVO vo = null;
		try {
			ProcessReleaseNotes note = this.systemProcessReleaseNotesService
					.getDetailByProcessDefinitionId(entity.getId());
			vo = new ProcessDefinitionVO(entity,
					this.systemProcessReleaseNotesService.convertToVO(note));
		} catch (ObjectNotFoundException e) {
			e.printStackTrace();
		}
		return vo;
	}

	/**
	 * @see com.wilmar.process.core.services.api.IBaseService#convertToVOList(java.util.List)
	 */
	public List<ProcessDefinitionVO> convertToVOList(
			Collection<ProcessDefinition> entityCollection) {
		List<ProcessDefinitionVO> voList = new ArrayList<ProcessDefinitionVO>(
				entityCollection.size());
		for (ProcessDefinition pd : entityCollection) {
			voList.add(this.convertToVO(pd));
		}
		return voList;
	}

	/**
	 * @return the systemProcessAuthorityService
	 */
	public ISystemProcessAuthorityService getSystemProcessAuthorityService() {
		return systemProcessAuthorityService;
	}

	/**
	 * @param systemProcessAuthorityService
	 *            the systemProcessAuthorityService to set
	 */
	public void setSystemProcessAuthorityService(
			ISystemProcessAuthorityService systemProcessAuthorityService) {
		this.systemProcessAuthorityService = systemProcessAuthorityService;
	}

	/**
	 * @see com.wilmar.process.core.services.api.ISystemProcessDefinitionService#getStartFormByProcessDefinitionKey(java.lang.String)
	 */
	public String getStartFormByProcessDefinitionKey(String processDefinitionKey)
			throws ObjectNotFoundException {
		List<ProcessDefinition> allList = this.processEngine
				.getRepositoryService().createProcessDefinitionQuery()
				.processDefinitionKey(processDefinitionKey).notSuspended()
				.orderDesc(ProcessDefinitionQuery.PROPERTY_VERSION).list();
		if (allList == null || allList.size() == 0)
			throw new ObjectNotFoundException(
					"There are not any process definition by key:"
							+ processDefinitionKey);
		ProcessDefinition pd = allList.get(0);
		List<String> startNames = this.processEngine.getRepositoryService()
				.getStartActivityNames(pd.getId());
		int startNodeCount = startNames.size();
		if (startNodeCount == 0)
			throw new ObjectNotFoundException(
					"This process definition didn't set any start node!");
		else if (startNodeCount > 1) {
			log
					.warn("There is more than 1 start node definded in this process definition!");
		}
		return this.processEngine.getRepositoryService()
				.getStartFormResourceName(pd.getId(), startNames.get(0));
	}

	/**
	 * @see com.wilmar.process.core.services.api.ISystemProcessDefinitionService#getProcessDescriptionById(java.lang.String)
	 */
	public String getProcessDescriptionById(String processDefinitionId)
			throws ObjectNotFoundException {
		ProcessDefinition pd = this.processEngine.getRepositoryService()
				.createProcessDefinitionQuery().processDefinitionId(
						processDefinitionId).uniqueResult();
		if (pd == null)
			throw new ObjectNotFoundException(
					"Here is no process definition, the id is:"
							+ processDefinitionId);
		return pd.getDescription();
	}

	/**
	 * @see com.wilmar.process.core.services.api.ISystemProcessDefinitionService#deployProcessDefinition(java.lang.String,
	 *      com.wilmar.process.entity.ProcessReleaseNotes)
	 */
	public void deployProcessDefinition(String definitionString,
			ProcessReleaseNotes processReleaseNotes) {
		NewDeployment deploy = this.processEngine.getRepositoryService()
				.createDeployment();
		deploy = deploy.addResourceFromString("", definitionString);
		deploy.deploy();
	}

	/**
	 * @return the processEngine
	 */
	public ProcessEngine getProcessEngine() {
		return processEngine;
	}

	/**
	 * @param processEngine
	 *            the processEngine to set
	 */
	public void setProcessEngine(ProcessEngine processEngine) {
		this.processEngine = processEngine;
	}

}
